#pragma once

#include <PlayFab<%- gemName %>Sdk/PlayFabBaseModel.h>
#include <aws/core/utils/json/JsonSerializer.h>

using namespace rapidjson;

namespace PlayFab<%- gemName %>Sdk
{
    namespace <%- api.name %>Models
    {
<% for(var i in datatypes) { var datatype = datatypes[i]; // ========= BEGIN FOR-EACH DATATYPE DEFN =========
if(datatype.isenum) { // ========= BEGIN EACH ENUM =========
%>        enum <%- datatype.name %>
        {
            <% for(var i=0; i<datatype.enumvalues.length-1; i++) { var enumval = datatype.enumvalues[i] %><%- datatype.name %><%- enumval.name %>,
            <% } %><%- datatype.name %><%- datatype.enumvalues[datatype.enumvalues.length-1].name %>
        };

        inline void write<%- datatype.name %>EnumJSON(<%- datatype.name %> enumVal, PFStringJsonWriter& writer)
        {
            switch (enumVal)
            {
<% for(var i=0; i<datatype.enumvalues.length; i++) { var enumval = datatype.enumvalues[i]
%>            case <%- datatype.name %><%- enumval.name %>: writer.String("<%- enumval.name %>"); break;
<% } %>
            }
        }

        inline <%- datatype.name %> read<%- datatype.name %>FromValue(const rapidjson::Value& obj)
        {
            // #THIRD_KIND_PLAYFAB_GAME_STATE_DESERIALISATION_FIX: - The json response from the server for some enums may still be numeric
            if (obj.IsNumber())
                return static_cast<<%- datatype.name %>>(obj.GetInt());

            static std::map<const char *, <%- datatype.name %>, PlayFab<%- gemName %>Sdk::StringCompare> _<%- datatype.name %>Map;
            if (_<%- datatype.name %>Map.size() == 0)
            {
                // Auto-generate the map on the first use
<% for(var i=0; i<datatype.enumvalues.length; i++) { var enumval = datatype.enumvalues[i]
%>                _<%- datatype.name %>Map["<%- datatype.enumvalues[i].name %>"] = <%- datatype.name %><%- datatype.enumvalues[i].name %>;
<% } %>
            }

            auto output = _<%- datatype.name %>Map.find(obj.GetString());
            if (output != _<%- datatype.name %>Map.end())
                return output->second;

            return <%- datatype.name %><%- datatype.enumvalues[0].name %>; // Basically critical fail
        }

<% } // ========= END EACH ENUM =========
else { // ========= BEGIN EACH OBJECT =========
%>        struct <%- datatype.name %> : public <%- GetDatatypeBaseType(datatype) %>
        {
<% for(var i in datatype.properties) { var property = datatype.properties[i]
%>            <%- GetPropertyDef(property, datatype) %>
<% } %>
            <%- datatype.name %>() :
                <%- GetDatatypeBaseType(datatype) %>()<% for(var i in datatype.properties) { var property = datatype.properties[i] %>,
                <%- GetPropertySafeName(property) %>(<%- GetPropertyDefaultValue(property, datatype) %>)<% } %>
            {}

            <%- datatype.name %>(const <%- datatype.name %>& src) :
                <%- GetDatatypeBaseType(datatype) %>()<% for(var i in datatype.properties) { var property = datatype.properties[i] %>,
                <%- GetPropertySafeName(property) %>(<%- GetPropertyCopyValue(property, datatype) %>)<% } %>
            {}

            <%- datatype.name %>(const rapidjson::Value& obj) : <%- datatype.name %>()
            {
                readFromValue(obj);
            }

            ~<%- datatype.name %>()
            {
<% for(var p in datatype.properties) { %><%- GetPropertyDestructor("                ", datatype.properties[p]) %><% }
%>            }

            void writeJSON(PFStringJsonWriter& writer) const override
            {
                writer.StartObject();
<% for(var p in datatype.properties) { var property = datatype.properties[p];
%><%- GetPropertySerializer("                ", property, datatype) %>
<% }
%>                writer.EndObject();
            }

            bool readFromValue(const rapidjson::Value& obj) override
            {
<% for(var p in datatype.properties) { var property = datatype.properties[p];
%><%- GetPropertyDeserializer("                ", property, datatype) %>
<% } %>
                return true;
            }
        };

<% } // ========= END EACH OBJECT =========
} // ========= END FOR-EACH DATATYPE DEFN ========= %>
    }
}
